/*
 * Created on 18/11/2005
 */
package bancosys.tec.persist.persister;

import java.util.Collections;
import java.util.Set;

import jmine.tec.persist.PersistMessages;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.authorization.AuthorizationContext;
import bancosys.tec.persist.exception.PersistenceException;
import bancosys.tec.persist.hibernate.PartitionSessionFactory;
import bancosys.tec.persist.persister.listener.PurePersisterListener;
import bancosys.tec.persist.validator.Validator;

/**
 * Persiste o bean sem executar lógicas de negócio, apenas validações e remoções de dependências, direto na partição quente.
 * 
 * @param <T> classe do bean persistido.
 * @author Alexandre
 */
public class PurePersister<T extends Persistable> extends BasePersistAdapter<T> {

    private Set<? extends PurePersisterListener<? super T>> persisterListeners = Collections.emptySet();

    /**
     * Remove o bean.
     */
    @Override
    public void remove() {
        this.remove(AuthorizationContext.hot);
    }

    /**
     * Salva o bean
     */
    @Override
    public void save() {
        this.save(AuthorizationContext.hot);
    }

    /**
     * Executa a remoção em uma partição específica (quente/fria).
     * 
     * @param partition partição.
     */
    public void remove(AuthorizationContext partition) {
        Session session = this.getSession();
        T bean = this.getTarget();
        Validator<T> validator = this.getValidator();
        AuthorizationContext oldPartition = this.changePartition(partition, session);
        try {
            this.removeDependencies(bean);
            for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                purePersisterListener.beforeDelete(bean);
            }
            this.beforeRemove(validator, bean);
            this.audit(bean, PersistenceAction.remove);

            session.delete(this.getTarget());
            this.afterRemove(validator, bean);
            for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                purePersisterListener.afterDelete(bean);
            }
        } catch (HibernateException e) {
            throw new PersistenceException(PersistMessages.ERROR_REMOVING_BEAN.create(bean.getClass().getName()), e);
        }
        this.changePartition(oldPartition, session);
    }

    /**
     * Roda o audit manager e verifica se a pk é long
     * 
     * @param bean T
     * @param action {@link PersistenceAction}
     */
    private void audit(T bean, PersistenceAction action) {
        if (bean.getPk() instanceof Long) {
            this.audit(bean, action, bean.getPk());
        } else {
            this.audit(bean, action, null);
        }
    }

    /**
     * Salva em uma partição específica (quente/fria).
     * 
     * @param partition partição.
     */
    public void save(AuthorizationContext partition) {
        Session session = this.getSession();
        T bean = this.getTarget();
        Validator<T> validator = this.getValidator();
        AuthorizationContext oldPartition = this.changePartition(partition, session);

        try {
            if (PersistenceAction.insert.equals(this.insertOrUpdate())) {
                this.beforeSave(validator, bean);
                for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                    purePersisterListener.beforeInsert(bean);
                }
                this.getSession().saveOrUpdate(bean);

                this.audit(bean, PersistenceAction.insert);

                this.afterSave(validator, bean);
                for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                    purePersisterListener.afterInsert(bean);
                }
            } else {
                this.beforeUpdate(validator, bean);
                for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                    purePersisterListener.beforeUpdate(bean);
                }
                this.getSession().saveOrUpdate(bean);

                this.audit(bean, PersistenceAction.update);

                this.afterUpdate(validator, bean);
                for (PurePersisterListener<? super T> purePersisterListener : this.persisterListeners) {
                    purePersisterListener.afterUpdate(bean);
                }
            }
        } catch (HibernateException e) {
            throw new PersistenceException(PersistMessages.ERROR_SAVING_BEAN.create(bean.getClass().getName()), e);
        }
        this.changePartition(oldPartition, session);
    }

    /**
     * Troca de partição
     * 
     * @param partition partição destino, se for <code>null</code> não executa a troca.
     * @param session session hibernate.
     * @return partição anterior ou <code>null</code> quando a troca de partição não é suportada.
     */
    private AuthorizationContext changePartition(AuthorizationContext partition, Session session) {
        SessionFactory sessionFactory = this.getSessionFactory();
        if (partition != null && sessionFactory instanceof PartitionSessionFactory) {
            PartitionSessionFactory partitionSessionFactory = (PartitionSessionFactory) sessionFactory;
            return partitionSessionFactory.changeSessionAuthorizationContext(session, partition);
        }
        return null;
    }

    /**
     * Remove as dependencias do bean
     * 
     * @param bean bean
     */
    @SuppressWarnings("unchecked")
    protected void removeDependencies(T bean) {
        Persister<T> persister = (Persister<T>) bean.getPersister();
        if (persister instanceof DefaultPersister) {
            DefaultPersister<T> defaultPersister = (DefaultPersister<T>) persister;
            defaultPersister.removeDependencies(bean);
        }
    }

    /**
     * @param persisterListeners the persisterListeners to set
     */
    void setPersisterListeners(Set<? extends PurePersisterListener<? super T>> persisterListeners) {
        this.persisterListeners = persisterListeners;
    }
}
